package sf.database.mapper.handle;

import sf.common.exception.SmallOrmException;
import sf.database.mapper.annotation.ExecuteProvider;
import sf.tools.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 以下注解的功能实现.
 * @see sf.database.mapper.annotation.ExecuteProvider
 */
public class MethodHandleProvider {
    private static final Map<Class, Object> PROVIDERS_CACHE = new ConcurrentHashMap<>();

    public static String getSQLTemplateProvider(ExecuteProvider sqlProvider, Method owner, Object[] args) {
        Class<?> providerCls = null;
        String providerMethodName = null;
        try {
            providerCls = sqlProvider.type();
            Object provider = PROVIDERS_CACHE.get(providerCls);
            if (provider == null) {
                provider = providerCls.getDeclaredConstructor().newInstance();
                PROVIDERS_CACHE.put(providerCls, provider);
            }
            providerMethodName = sqlProvider.method();
            if (StringUtils.isBlank(providerMethodName)) {
                providerMethodName = owner.getName();
            }
            Method providerMethod = providerCls.getMethod(providerMethodName, owner.getParameterTypes());
            if (providerMethod.getReturnType() != String.class) {
                throw new SmallOrmException(SmallOrmException.ANNOTATION_DEFINE_ERROR, "SqlProvider类[" + providerCls.getName() + "]的方法[" + providerMethodName + "]返回值必须为SqlReady类型");
            }
            providerMethod.setAccessible(Boolean.TRUE);
            return (String) providerMethod.invoke(provider, args);

        } catch (IllegalAccessException | InstantiationException e) {
            throw new SmallOrmException(SmallOrmException.ANNOTATION_DEFINE_ERROR, "实例化" + providerCls.getName() + "失败，请检查是否有公有的无参构造");
        } catch (InvocationTargetException e) {
            throw new SmallOrmException(SmallOrmException.ANNOTATION_DEFINE_ERROR, "调用Provder方法出错" + e.getMessage(), e);
        } catch (NoSuchMethodException e) {
            throw new SmallOrmException(SmallOrmException.ANNOTATION_DEFINE_ERROR, "未能从" + providerCls.getName() + "获取到" + providerMethodName + " 方法");
        }
    }
}
